﻿using System.Collections;
using System.Diagnostics;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using DimensionsHelper.Common.Contracts.Excel;

namespace DimensionsHelper.Client.Controls;

public class BorderGridRange : FrameworkElement, IList, IAddChild
{
    public static readonly DependencyProperty BorderStyleProperty =
        DependencyProperty.Register(
            nameof(BorderStyle),
            typeof(BorderStyle),
            typeof(BorderGridRange),
            new FrameworkPropertyMetadata(BorderStyle.None, FrameworkPropertyMetadataOptions.AffectsRender,
                OnBorderPropertyChanged));


    public static readonly DependencyProperty HairBorderThicknessProperty =
        DependencyProperty.Register(
            nameof(HairBorderThickness),
            typeof(double),
            typeof(BorderGridRange),
            new PropertyMetadata(0.5d, GetThicknessPropertyChangedCallback(ThicknessType.Hair)));


    public static readonly DependencyProperty NormalBorderThicknessProperty =
        DependencyProperty.Register(
            nameof(NormalBorderThickness),
            typeof(double),
            typeof(BorderGridRange),
            new PropertyMetadata(1d, GetThicknessPropertyChangedCallback(ThicknessType.Normal)));


    public static readonly DependencyProperty MediumBorderThicknessProperty =
        DependencyProperty.Register(
            nameof(MediumBorderThickness),
            typeof(double),
            typeof(BorderGridRange),
            new PropertyMetadata(1.5d, GetThicknessPropertyChangedCallback(ThicknessType.Medium)));


    public static readonly DependencyProperty ThickBorderThicknessProperty =
        DependencyProperty.Register(
            nameof(ThickBorderThickness),
            typeof(double),
            typeof(BorderGridRange),
            new PropertyMetadata(2d, GetThicknessPropertyChangedCallback(ThicknessType.Thick)));


    public static readonly DependencyProperty BorderBrushProperty =
        DependencyProperty.Register(
            nameof(BorderBrush),
            typeof(Brush),
            typeof(BorderGridRange),
            new PropertyMetadata(Brushes.LightGray));


    public static readonly DependencyProperty AddHorizontalBorderProperty =
        DependencyProperty.Register(
            nameof(AddHorizontalBorder),
            typeof(bool),
            typeof(BorderGridRange),
            new PropertyMetadata(false, OnBorderPropertyChanged));


    public static readonly DependencyProperty AddVerticalBorderProperty =
        DependencyProperty.Register(
            nameof(AddVerticalBorder),
            typeof(bool),
            typeof(BorderGridRange),
            new PropertyMetadata(false, OnBorderPropertyChanged));


    public static readonly DependencyProperty AddAroundBorderProperty =
        DependencyProperty.Register(
            nameof(AddAroundBorder),
            typeof(bool),
            typeof(BorderGridRange),
            new PropertyMetadata(false, OnBorderPropertyChanged));


    public static readonly DependencyProperty IsMergedProperty =
        DependencyProperty.Register(
            nameof(IsMerged),
            typeof(bool),
            typeof(BorderGridRange),
            new PropertyMetadata(false, OnIsMergedPropertyChanged));


    public static readonly DependencyProperty IsHiddenProperty =
        DependencyProperty.Register(
            nameof(IsHidden),
            typeof(bool),
            typeof(BorderGridRange),
            new PropertyMetadata(false, OnIsHiddenPropertyChanged));

    private readonly List<BorderGridRange> _children = new();
    private readonly List<(int X, int Y)> _indexMap = new();


    private BorderGridCellMatrix? _cells;


    private ThicknessType _currentThicknessType = ThicknessType.None;


    private bool _isInitialized;

    public int StartRow { get; set; }

    public int StartColumn { get; set; }

    public int EndRow { get; set; }

    public int EndColumn { get; set; }

    public List<int> SkipRows { get; set; } = [];
    public List<int> SkipColumns { get; set; } = [];

    public BorderGridRangeHideOrientation HideOrientation { get; set; } = BorderGridRangeHideOrientation.Row;

    public BorderGridLineRenderer LineRenderer { get; } = new();

    public BorderGridRange? OwnerRange { get; private set; }

    public BorderGridRangeCollection? Collection { get; set; }

    protected override int VisualChildrenCount => _children.Count + 1;

    public BorderStyle BorderStyle
    {
        get => (BorderStyle)GetValue(BorderStyleProperty);
        set => SetValue(BorderStyleProperty, value);
    }


    public double HairBorderThickness
    {
        get => (double)GetValue(HairBorderThicknessProperty);
        set => SetValue(HairBorderThicknessProperty, value);
    }


    public double NormalBorderThickness
    {
        get => (double)GetValue(NormalBorderThicknessProperty);
        set => SetValue(NormalBorderThicknessProperty, value);
    }


    public double MediumBorderThickness
    {
        get => (double)GetValue(MediumBorderThicknessProperty);
        set => SetValue(MediumBorderThicknessProperty, value);
    }


    public double ThickBorderThickness
    {
        get => (double)GetValue(ThickBorderThicknessProperty);
        set => SetValue(ThickBorderThicknessProperty, value);
    }


    public Brush BorderBrush
    {
        get => (Brush)GetValue(BorderBrushProperty);
        set => SetValue(BorderBrushProperty, value);
    }


    public bool AddHorizontalBorder
    {
        get => (bool)GetValue(AddHorizontalBorderProperty);
        set => SetValue(AddHorizontalBorderProperty, value);
    }


    public bool AddVerticalBorder
    {
        get => (bool)GetValue(AddVerticalBorderProperty);
        set => SetValue(AddVerticalBorderProperty, value);
    }


    public bool AddAroundBorder
    {
        get => (bool)GetValue(AddAroundBorderProperty);
        set => SetValue(AddAroundBorderProperty, value);
    }


    public bool IsMerged
    {
        get => (bool)GetValue(IsMergedProperty);
        set => SetValue(IsMergedProperty, value);
    }


    public bool IsHidden
    {
        get => (bool)GetValue(IsHiddenProperty);
        set => SetValue(IsHiddenProperty, value);
    }


    private bool IsEmpty => _cells == null || _cells.SizeX == 0 || _cells.SizeY == 0;


    public void AddChild(object value)
    {
        BorderGridRange child = (BorderGridRange)value;

        if (child.StartRow < StartRow || child.EndRow > EndRow ||
            child.StartColumn < StartColumn || child.EndColumn > EndColumn)
        {
            throw new ArgumentException("Range has invalid rows or columns.");
        }

        if (_cells != null && !child._isInitialized)
        {
            child.Initialize(_cells, true);
        }

        child.OwnerRange = this;

        _children.Add(child);
        BindChildProperty(child);
    }


    public void AddText(string text)
    {
        throw new NotSupportedException();
    }

    public int Count => (EndColumn - StartColumn + 1) * (EndRow - StartRow + 1);

    public bool IsFixedSize => false;

    public bool IsReadOnly => false;

    public bool IsSynchronized => false;

    public object SyncRoot => throw new NotSupportedException();


    public object? this[int index]
    {
        get => _children[index];

        set
        {
            if (value is BorderGridRange range)
            {
                _children.Add(range);
            }
        }
    }

    protected override Visual GetVisualChild(int index)
    {
        return index == 0 ? LineRenderer : _children[index - 1].LineRenderer;
    }

    internal BorderGridCell GetCell(int index)
    {
        Debug.Assert(_cells != null);
        return _cells[_indexMap[index].X, _indexMap[index].Y];
    }

    private static PropertyChangedCallback GetThicknessPropertyChangedCallback(ThicknessType thicknessType)
    {
        return (o, _) =>
        {
            if (o is BorderGridRange range && range._currentThicknessType == thicknessType)
            {
                range.UpdateBorder();
            }
        };
    }


    private static void OnBorderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;
        range._currentThicknessType = GetThicknessType(range.BorderStyle);
        range.UpdateBorder();
    }


    private static void OnIsMergedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (range._children.Count > 0)
        {
            foreach (BorderGridRange child in range._children)
            {
                if ((bool)e.NewValue)
                {
                    child.Merge();
                }
                else
                {
                    child.UnMerge();
                }
            }
        }
        else
        {
            if ((bool)e.NewValue)
            {
                range.Merge();
            }
            else
            {
                range.UnMerge();
            }
        }
    }


    private static void OnIsHiddenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;
        range.HideOrUnHide((bool)e.NewValue);
    }


    private static ThicknessType GetThicknessType(BorderStyle style)
    {
        switch (style)
        {
            case BorderStyle.None:
                break;

            case BorderStyle.Hair:
                return ThicknessType.Hair;

            case BorderStyle.Medium:
            case BorderStyle.MediumDashDotDot:
            case BorderStyle.MediumDashed:
            case BorderStyle.MediumDashDot:
                return ThicknessType.Medium;

            case BorderStyle.Thick:
                return ThicknessType.Thick;
        }

        return ThicknessType.Normal;
    }


    internal Pen? GetPen()
    {
        Brush brush = BorderBrush;

        return BorderStyle switch
        {
            BorderStyle.Hair => BorderGridPens.GetPen(brush, HairBorderThickness),
            BorderStyle.Dotted => BorderGridPens.GetDotPen(brush, NormalBorderThickness),
            BorderStyle.DashDot => BorderGridPens.GetDashDotPen(brush, NormalBorderThickness),
            BorderStyle.Double or BorderStyle.Thin => BorderGridPens.GetPen(brush, NormalBorderThickness),
            BorderStyle.DashDotDot => BorderGridPens.GetDashDotDotPen(brush, NormalBorderThickness),
            BorderStyle.Dashed => BorderGridPens.GetDashedPen(brush, NormalBorderThickness),
            BorderStyle.MediumDashDotDot => BorderGridPens.GetDashDotDotPen(brush, MediumBorderThickness),
            BorderStyle.MediumDashed => BorderGridPens.GetDashedPen(brush, MediumBorderThickness),
            BorderStyle.MediumDashDot => BorderGridPens.GetDashDotPen(brush, MediumBorderThickness),
            BorderStyle.Thick => BorderGridPens.GetPen(brush, ThickBorderThickness),
            BorderStyle.Medium => BorderGridPens.GetPen(brush, MediumBorderThickness),
            _ => null
        };
    }


    private void UpdateBorder()
    {
        BorderGridRange? range = this;

        while (range.OwnerRange != null)
        {
            range = range.OwnerRange;
        }

        range.Collection?.UpdateBorder();
    }


    internal void Initialize(BorderGridCellMatrix matrix, bool isChildRange = false)
    {
        if (_isInitialized)
        {
            return;
        }

        _cells = matrix;

        if (!isChildRange)
        {
            // 二维索引转一维索引时，以横向为基计算。

            for (int r = StartRow; r <= EndRow; r++)
            {
                if (SkipRows.Contains(r))
                {
                    continue;
                }

                for (int c = StartColumn; c <= EndColumn; c++)
                {
                    if (SkipColumns.Contains(c))
                    {
                        continue;
                    }

                    BorderGridCell cell = _cells[r, c];
                    cell.Range = this;

                    if (r == StartRow)
                    {
                        cell.AddFlags(BorderCellFlags.TopBound);
                    }

                    if (r == EndRow)
                    {
                        cell.AddFlags(BorderCellFlags.BottomBound);
                    }

                    if (c == StartColumn)
                    {
                        cell.AddFlags(BorderCellFlags.LeftBound);
                    }

                    if (c == EndColumn)
                    {
                        cell.AddFlags(BorderCellFlags.RightBound);
                    }

                    AddVisualChild(cell);
                    _indexMap.Add((r, c));
                }
            }

            // 初始化子区间
            foreach (BorderGridRange child in _children)
            {
                child.Initialize(matrix, true);
            }
        }

        _isInitialized = true;
    }


    private void Merge()
    {
        if ((EndRow <= StartRow && EndColumn <= StartColumn) || _cells == null)
        {
            return;
        }

        for (int i = StartRow; i <= EndRow; i++)
        {
            for (int j = StartColumn; j <= EndColumn; j++)
            {
                BorderGridCell cell = _cells[i, j];

                // 因为中间的单元格只绘制左、上两个方位的边框，因此，
                // 最后一行和最后一列也需要加上 MergedColumnInner 和 MergedLineInner
                if (i > StartRow)
                {
                    cell.AddFlags(BorderCellFlags.MergedLineInner);
                }

                if (j > StartColumn)
                {
                    cell.AddFlags(BorderCellFlags.MergedColumnInner);
                }
            }
        }

        UpdateBorder();
    }


    private void UnMerge()
    {
        if ((EndRow <= StartRow && EndColumn <= StartColumn) || _cells == null)
        {
            return;
        }

        for (int i = StartRow; i <= EndRow; i++)
        {
            for (int j = StartColumn; j <= EndColumn; j++)
            {
                _cells[i, j].RemoveFlags(BorderCellFlags.Merged);
            }
        }

        UpdateBorder();
    }


    private void HideOrUnHideRow(int row, bool hide)
    {
        if (_cells == null)
        {
            return;
        }

        BorderGridCell cell = _cells[row, StartColumn];

        if (hide)
        {
            cell.AddFlags(BorderCellFlags.Hidden);
        }
        else
        {
            cell.RemoveFlags(BorderCellFlags.Hidden);
        }

        // left
        BorderGridCell? left = cell.Left;
        while (left != null)
        {
            if (hide)
            {
                left.AddFlags(BorderCellFlags.Hidden);
            }
            else
            {
                left.RemoveFlags(BorderCellFlags.Hidden);
            }

            left = left.Left;
        }

        // right
        BorderGridCell? right = cell.Right;
        while (right != null)
        {
            if (hide)
            {
                right.AddFlags(BorderCellFlags.Hidden);
            }
            else
            {
                right.RemoveFlags(BorderCellFlags.Hidden);
            }

            right = right.Right;
        }
    }


    private void HideOrUnHideColumn(int column, bool hide)
    {
        if (_cells == null)
        {
            return;
        }

        BorderGridCell cell = _cells[StartRow, column];

        if (hide)
        {
            cell.AddFlags(BorderCellFlags.Hidden);
        }
        else
        {
            cell.RemoveFlags(BorderCellFlags.Hidden);
        }

        // top
        BorderGridCell? top = cell.Top;
        while (top != null)
        {
            if (hide)
            {
                top.AddFlags(BorderCellFlags.Hidden);
            }
            else
            {
                top.RemoveFlags(BorderCellFlags.Hidden);
            }

            top = top.Top;
        }

        // bottom
        BorderGridCell? bottom = cell.Bottom;
        while (bottom != null)
        {
            if (hide)
            {
                bottom.AddFlags(BorderCellFlags.Hidden);
            }
            else
            {
                bottom.RemoveFlags(BorderCellFlags.Hidden);
            }

            bottom = bottom.Bottom;
        }
    }


    private void HideOrUnHide(bool hide)
    {
        if (IsEmpty || _cells == null)
        {
            return;
        }

        if (HideOrientation == BorderGridRangeHideOrientation.Row)
        {
            for (int r = StartRow; r <= EndRow; r++)
            {
                HideOrUnHideRow(r, hide);
            }
        }
        else
        {
            for (int c = StartColumn; c <= EndColumn; c++)
            {
                HideOrUnHideColumn(c, hide);
            }
        }

        UpdateBorder();
    }


    internal BorderGridRange? FindChild(BorderGridCell cell)
    {
        return _children.Find(r => cell.Row >= r.StartRow
                                   && cell.Row <= r.EndRow
                                   && cell.Column >= r.StartColumn
                                   && cell.Column <= r.EndColumn);
    }


    private void BindChildProperty(BorderGridRange child)
    {
        BindingOperations.SetBinding(child,
            AddAroundBorderProperty,
            new Binding(nameof(AddAroundBorder)) { Source = this, Mode = BindingMode.OneWay });

        BindingOperations.SetBinding(child,
            AddHorizontalBorderProperty,
            new Binding(nameof(AddHorizontalBorder)) { Source = this, Mode = BindingMode.OneWay });

        BindingOperations.SetBinding(child,
            AddVerticalBorderProperty,
            new Binding(nameof(AddVerticalBorder)) { Source = this, Mode = BindingMode.OneWay });
    }


    private enum ThicknessType
    {
        None,
        Hair,
        Normal,
        Medium,
        Thick
    }


    #region IList

    public int Add(object? value)
    {
        if (value is BorderGridRange range)
        {
            if (!range._isInitialized && _cells != null)
            {
                range.Initialize(_cells, true);
            }

            range.OwnerRange = this;

            _children.Add(range);
            BindChildProperty(range);
            return _children.Count - 1;
        }

        return -1;
    }

    public void Clear()
    {
        _children.Clear();
    }

    public bool Contains(object? value)
    {
        if (value is BorderGridRange range)
        {
            return _children.Contains(range);
        }

        return false;
    }

    public int IndexOf(object? value)
    {
        if (value is BorderGridRange range)
        {
            return _children.IndexOf(range);
        }

        return -1;
    }

    public void Insert(int index, object? value)
    {
        if (value is BorderGridRange range)
        {
            _children.Insert(index, range);
        }
    }

    public void Remove(object? value)
    {
        if (value is BorderGridRange range)
        {
            _children.Remove(range);
        }
    }

    public void RemoveAt(int index)
    {
        _children.RemoveAt(index);
    }

    public void CopyTo(Array array, int index)
    {
        _children.CopyTo((BorderGridRange[])array, index);
    }

    public IEnumerator GetEnumerator()
    {
        return _children.GetEnumerator();
    }

    #endregion
}


public enum BorderGridRangeHideOrientation
{
    /// <summary>
    ///     区间隐藏时，隐藏整行，不隐藏列
    /// </summary>
    Row,

    /// <summary>
    ///     区间隐藏时，隐藏整列，不隐藏行
    /// </summary>
    Column
}